package util

import (
	"bytes"
	"fmt"
	"gitee.com/kingzyt/common/log"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"
)

func SplitOnIntListCore(module string, idsStr string, sep string, proc func(id int32) bool) error {
	if proc == nil {
		return nil
	}

	ids := strings.Split(idsStr, sep)
	for i := 0; i < len(ids); i++ {
		intStr := strings.TrimSpace(ids[i])
		if intStr == "" {
			continue
		}

		id, err := strconv.Atoi(intStr)
		if err != nil {
			return fmt.Errorf("%s; id is not number, %s", module, intStr)
		} else {
			if !proc(int32(id)) {
				break
			}
		}
	}

	return nil
}

func SplitOnIntList(module string, idsStr string, proc func(id int32) bool) error {
	return SplitOnIntListCore(module, idsStr, ",", proc)
}
func SplitOnIntListSemicolon(module string, idsStr string, proc func(id int32) bool) error {
	return SplitOnIntListCore(module, idsStr, ";", proc)
}
func SplitIntsGroup(module string, strsStr string, proc func(ints []int32) bool) (err error) {
	if proc == nil {
		return nil
	}
	SplitOnStrListSemicolon(module, strsStr, func(idsStr string) bool {
		var ints []int32
		err = SplitOnIntList(module, idsStr, func(id int32) bool {
			ints = append(ints, id)
			return true
		})
		if err != nil {
			return false
		}
		return proc(ints)
	})
	return
}
func Split2IntsGroup(module string, strsStr string, proc func(ints []int32) bool) (err error) {
	if proc == nil {
		return nil
	}
	idx := 0
	SplitOnStrListSemicolon(module, strsStr, func(idsStr string) bool {
		var ints []int32
		err = SplitOnIntList(module, idsStr, func(id int32) bool {
			ints = append(ints, id)
			return true
		})
		if err != nil {
			return false
		}
		if len(ints) != 2 {
			err = fmt.Errorf("ints len is not 2 at group %d", idx)
			return false
		}
		idx++
		return proc(ints)
	})
	return
}

func SplitOnInt64List(module string, idsStr string, proc func(id int64) bool) error {
	if proc == nil {
		return nil
	}

	ids := strings.Split(idsStr, ",")
	var errInfo strings.Builder
	for i := 0; i < len(ids); i++ {
		intStr := strings.TrimSpace(ids[i])
		if intStr == "" {
			continue
		}

		id, err := strconv.Atoi(intStr)
		if err != nil {
			info := fmt.Sprintf("id is not number, [%s]", intStr)
			errInfo.WriteString(info)
			errInfo.WriteString("\n")
			log.Error(module, info)
		} else {
			if !proc(int64(id)) {
				break
			}
		}
	}
	if errInfo.String() != "" {
		return fmt.Errorf(errInfo.String())
	}
	return nil
}

func SplitOnStrList2(strsStr string) (strs []string) {
	strs = []string{}
	SplitOnStrList("", strsStr, func(str string) bool {
		strs = append(strs, str)
		return true
	})
	return
}

func SplitOnStrList(module string, strsStr string, proc func(str string) bool) {
	SplitOnStrListCore(module, strsStr, ",", proc)
}
func SplitOnStrListSemicolon(module string, strsStr string, proc func(str string) bool) {
	SplitOnStrListCore(module, strsStr, ";", proc)
}
func GetStrListSemicolonStrSubCnt(module string, strsStr string) (count int32) {
	SplitOnStrListCore(module, strsStr, ";", func(str string) bool {
		count++
		return true
	})
	return
}
func SplitOnStrListCore(module string, strsStr string, sep string, proc func(str string) bool) {
	if proc == nil {
		return
	}

	strs := strings.Split(strsStr, sep)
	for i := 0; i < len(strs); i++ {
		str := strings.TrimSpace(strs[i])
		if str == "" {
			continue
		}

		if !proc(str) {
			break
		}
	}
}
func SplitOnStrListCore2(strsStr string, sep string) []string {
	var rlt []string
	strs := strings.Split(strsStr, sep)
	for i := 0; i < len(strs); i++ {
		str := strings.TrimSpace(strs[i])
		if str == "" {
			continue
		}

		rlt = append(rlt, str)
	}
	return rlt
}

func IntListJoinsToString(intList []int32, sep string) string {
	rlt := bytes.Buffer{}
	for i := 0; i < len(intList); i++ {
		rlt.WriteString(strconv.Itoa(int(intList[i])))
		if i != len(intList)-1 {
			rlt.WriteString(sep)
		}
	}
	return rlt.String()
}
func Int64ListJoinsToString(intList []int64, sep string) string {
	rlt := bytes.Buffer{}
	for i := 0; i < len(intList); i++ {
		rlt.WriteString(strconv.FormatInt(intList[i], 10))
		if i != len(intList)-1 {
			rlt.WriteString(sep)
		}
	}
	return rlt.String()
}
func IdToCommaList(ids []int64) string {
	return Int64ListJoinsToString(ids, ",")
}

func StrListJoinsToString(strList []string, sep string) string {
	rlt := bytes.Buffer{}
	for i := 0; i < len(strList); i++ {
		rlt.WriteString(strList[i])
		if i != len(strList)-1 {
			rlt.WriteString(sep)
		}
	}
	return rlt.String()
}

func StringContains4BytesUtf8(str string) bool {
	for _, c := range str {
		if utf8.RuneLen(c) >= 4 {
			return true
		}
	}
	return false
}
func StringRemove4BytesUtf8(str string) string {
	var rlt []rune
	for _, c := range str {
		if utf8.RuneLen(c) >= 4 {
			continue
		} else {
			rlt = append(rlt, c)
		}
	}
	return string(rlt)
}

func StringRemoveDigits(str string) string {
	var rlt []rune
	runes := []rune(str)
	for _, c := range runes {
		if unicode.IsDigit(c) {
			continue
		} else {
			rlt = append(rlt, c)
		}
	}
	return string(rlt)
}

func SplitIntoEnAndCnWord(str string) ([]string, []string) {
	var cnss []*[]rune

	runes := []rune(str)
	for i, r := range runes {
		if unicode.IsSpace(r) {
			runes[i] = ' '
			r = ' '
		}
	}

	var cns *[]rune
	for i, r := range runes {
		//fmt.Printf("%c %x\n", r, int32(r))
		if 0x4e00 <= r && r <= 0x9fa5 {
			if cns == nil {
				var cnsnew []rune
				cns = &cnsnew
				cnss = append(cnss, cns)
			}
			*cns = append(*cns, r)
			runes[i] = ' '
		} else {
			cns = nil
		}
	}
	var rltCn []string
	for _, rs := range cnss {
		rltCn = append(rltCn, string(*rs))
	}

	ensstr := string(runes)
	var rltEn []string
	SplitOnStrListCore("", ensstr, " ", func(str string) bool {
		rltEn = append(rltEn, str)
		return true
	})

	return rltEn, rltCn
}

var spaceChars = []rune{'\r', '\n', ' ', '　', '\t'}

var unexceptedChars = []rune{'\r', '\n', ' ', '　', '\t', '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', '-', '_', '+', '=', '{', '}', '[', ']', '|', '\\', '"', '\'', ':', ';', '<', '>', ',', '.', '/', '?',
	'·', '！', '￥', '…', '—', '【', '】', '、', '：', '；', '‘', '“', '”', '’', '《', '》', '，', '。', '、', '？'}

func StrRemoveSpecialAndSpace(str string) string {
	return StrRemoveChars(str, unexceptedChars)
}
func StrRemoveSpace(str string) string {
	return StrRemoveChars(str, spaceChars)
}
func StrRemoveChars(str string, removingChars []rune) string {
	src := []rune(str)
	rlt := []rune{}
	for _, c := range src {
		found := false
		for _, unexceptedC := range removingChars {
			if c == unexceptedC {
				found = true
				break
			}
		}
		if !found {
			rlt = append(rlt, c)
		}
	}
	return string(rlt)
}

func IdCombinesIdToKey(id0, id1 int64) string {
	return strconv.FormatInt(id0, 36) + "_" + strconv.FormatInt(id1, 36)
}

func IdsToKey(ids ...int64) string {
	var sb strings.Builder
	for i := 0; i < len(ids); i++ {
		if i != 0 {
			sb.WriteString("_")
		}
		sb.WriteString(strconv.FormatInt(ids[i], 36))
	}
	return sb.String()
}

func CombineAny2ToKey(a interface{}, b interface{}) string {
	return fmt.Sprintf("%v_%v", a, b)
}
func CombineAny3ToKey(a, b, c interface{}) string {
	return fmt.Sprintf("%v_%v_%v", a, b, c)
}

func StrListContainsStr(strs []string, a string) bool {
	for _, str := range strs {
		if str == a {
			return true
		}
	}
	return false
}

func RemoveDupInShortStrList(strs []string) (rlt []string) {
	for i := 0; i < len(strs); i++ {
		isDup := false
		for j := 0; j < i; j++ {
			if strs[i] == strs[j] {
				isDup = true
				break
			}
		}
		if !isDup {
			rlt = append(rlt, strs[i])
		}
	}
	return
}

func RemoveDupInStrList(strs []string) (rlt []string) {
	mapStrs := map[string]struct{}{}
	for i := 0; i < len(strs); i++ {
		mapStrs[strs[i]] = struct{}{}
	}
	for str := range mapStrs {
		rlt = append(rlt, str)
	}
	return
}
func RemoveEmptyInStrList(strs []string) (rlt []string) {
	for i := 0; i < len(strs); i++ {
		a := strings.TrimSpace(strs[i])
		if a != "" {
			rlt = append(rlt, a)
		}
	}
	return
}

func StrRuneLen(str string) int {
	return len([]rune(str))
}

func CheckStrRuneLen(str string, n int) error {
	if StrRuneLen(str) > n {
		return fmt.Errorf("string length > %d: [%s]", n, ClampRuneStr(str, 50))
	}
	return nil
}

func ClampRuneStr(str string, n int) string {
	if len([]rune(str)) > n {
		return string(([]rune(str))[:n])
	}
	return str
}
